package FIT::Util::MultiLevelIndex;

use FIT::Class;

has keys  => (is => 'ro', isa => 'ArrayRef');
has index => (is => 'ro', isa => 'HashRef', default => sub { {} });

sub BUILD {
    my @result        = @{ shift->{query} };
    my @original_keys = @{ $self->keys };
    my $index         = $self->index;
    for my $item (@result) {
        my $last_index = $index;
        my @keys       = @original_keys;
        my $last_key   = pop @keys;
        for my $key (@keys)
            { $last_index = ($last_index->{ $item->$key } ||= {}) }
        $last_index->{ $item->$last_key } = $item;
    }
}

sub find {
    my $item  = shift;
    my @keys  = @{ $self->keys };
    my $index = $self->index;
    my $last_match;
    for my $key (@keys) {
        my $value = $item->$key;
        # BUG!
        # we are actually using string equality with no type adapters,
        # for comparing typed columns! a hash access is a string eq
        # should be given a type adapter for each key and use it's as_string
        # until this is fixed, expect problems in indexing complex types
        return undef unless exists $index->{$value};
        $last_match = $index;
        $index = $index->{$value};
    }
    delete $last_match->{ $item->{$keys[-1]} }; # remove the match
    return $index;
}

sub surplus { # items left after those matched were removed
    my @keys  = @{ $self->keys };
    my $index = $self->index;
    return get_values_n_level_down($index, scalar(@keys) - 1);
}

# flattens the hash, returning only leaf values
sub get_values_n_level_down() {
    my ($index, $n) = @_;
    local $_;
    return values(%$index) unless $n; # can be empty list for deleted values
    my @result = map { get_values_n_level_down($_, $n-1) } values %$index;
    return @result;
}


